我们的程序常常需要从配置文件中获取参数,一方面,用户关于程序的自定义配置往往就存储在配置文件中,自定义配置的适配与保存就转化为对配置文件的读写操作;另一方面,也是本文编写的由来,那就是将有关程序中核心算法的参数提取到配置文件,方便算法参数的调试(只要调整配置文件,无需修改程序源码,更无需重新构建)。
.ini 文件作为典型的配置文件,其文件存储的内容基本上是一些参数的 Key-Value 形式,此外就是一些所谓的属性段,多个对同一方面参数进行描述的 键值对 可以归纳为一个 Section。
下面,是 Win/Linux 下如何通过不同的 API 实现对 *.ini 配置文件读写操作的整理。
Linux, using Glib API
Reference:
Linux 下我们通过使用 Glib API 操作 .ini 文件,具体流程包括安装 Glib 库,加载 ini 文件,通过 Key 读写键值对,最后再卸载文件。
1. Configure Runtime Environment1
xxx@...$ sudo apt-get install libglib2.0-dev
Install glib-2.0 and add it as a link library for build option.
2. Create File Handler
APIs:
1
2
3
4
5
6
7
8
9 GKeyFile* g_key_file_new (void);
gboolean g_key_file_load_from_file (GKeyFile *key_file,
const gchar *file,
GKeyFileFlags flags,
GError **error);
flags: G_KEY_FILE_NONE
| G_KEY_FILE_KEEP_COMMENTS
| G_KEY_FILE_KEEP_TRANSLATIONSExamples:
1
2
3
4
5
6
7
8
9
10
11
12
13
// get Gkeyfile
GKeyFile* viewer_ini = g_key_file_new();
GError **error = NULL;
if (!g_key_file_load_from_file(viewer_ini, "velodyne.ini",
G_KEY_FILE_NONE, error)){
fprintf (stderr, "Could not read config file\n");
return EXIT_FAILURE;
}
3. Read/Write Parameters from/to File
我们选用常用的数据类型,integer, double, string, boolean。
APIs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 gint g_key_file_get_integer (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error);
void g_key_file_set_integer (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gint value);
gdouble g_key_file_get_double (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error);
void g_key_file_set_double (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gdouble value);
gchar* g_key_file_get_string (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error);
void g_key_file_set_string (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
const gchar *string);
gboolean g_key_file_get_boolean (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error);
void g_key_file_set_boolean (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gboolean value);Examples:
1
2
3
4
5
6
7 # velodyne.ini
[CfgGlobal]
GroundZ=-192
CarLength=469
CarWidth=185
CarHeight=172
Threshold=0.820000
1
2
3
4
5
6
7
8
9
10 // *.cpp
cfg.cfgGlobal.GroundZ =
g_key_file_get_integer(viewer_ini, "CfgGlobal", "GroundZ", error);
cfg.cfgGlobal.ThresholdMinRad =
g_key_file_get_double(viewer_ini, "CfgGlobal", "Threshold", error);
g_key_file_set_integer(viewer_ini, "CfgGlobal", "GroundZ",
cfg.cfgGlobal.GroundZ);
g_key_file_set_double(viewer_ini, "CfgGlobal", "ThresholdMinRad",
cfg.cfgGlobal.Threshold);
4. Free File Handler
API:
1 void g_key_file_free (GKeyFile *key_file);Example:
1 g_key_file_free (glib_keyfile);
Windows, using Winbase API
Windows 下我们使用 Winbase.h 提供的 API 操作 .ini 文件,与 Linux 下使用 Glib API 最大的不同是,不存在配置文件的加载,卸载流程,不过每次读写参数时都需要传递操作文件所需要的文件名;此外,支持的数据类型也比较少(似乎就只有 Int 和 String),写操作更是只有 String 数据类型。
下面是简单功能的基本实现。
APIs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 UINT WINAPI GetPrivateProfileInt(
_In_ LPCTSTR lpAppName,
_In_ LPCTSTR lpKeyName,
_In_ INT nDefault,
_In_ LPCTSTR lpFileName
);
DWORD WINAPI GetPrivateProfileString(
_In_ LPCTSTR lpAppName,
_In_ LPCTSTR lpKeyName,
_In_ LPCTSTR lpDefault,
_Out_ LPTSTR lpReturnedString,
_In_ DWORD nSize,
_In_ LPCTSTR lpFileName
);
BOOL WINAPI WritePrivateProfileString(
_In_ LPCTSTR lpAppName,
_In_ LPCTSTR lpKeyName,
_In_ LPCTSTR lpString,
_In_ LPCTSTR lpFileName
);Examples:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 // for GetPrivateProfileInt&Get/WritePrivateProfileString
// for _snprintf
// for atof
char buf[64];
char defaultVal[64];
cfg.cfgGlobal.GroundZ =
GetPrivateProfileInt("CfgGlobal", "GroundZ", g_DefaultGlobalCfg.GroundZ, "velodyne.ini");
_snprintf(defaultVal, sizeof(defaultVal), "%f", g_DefaultGlobalCfg.Threshold);
GetPrivateProfileString("CfgGlobal", "Threshold", defaultVal, buf, sizeof(buf), "velodyne.ini");
cfg.cfgGlobal.Threshold = (double)atof(buf);
char val[64];
_snprintf(val, sizeof(val), "%d", cfg.cfgGlobal.GroundZ);
WritePrivateProfileString("CfgGlobal", "GroundZ", val, "velodyne.ini");
_snprintf(val, sizeof(val), "%.6f", cfg.cfgGlobal.Threshold);
WritePrivateProfileString("CfgGlobal", "Threshold", val, "velodyne.ini");
注意:
- GetPrivateProfileInt 和 GetPrivateProfileString 都需要传递缺省值,而且每一次都需要制定对应的配置文件;假如成功找到对应的参数键,返回对应的值,否则返回传进去的缺省值。
- 对于 Int,String 除外的数据类型,通过 GetPrivateProfileString 获取参数键对应的值,不过返回的值是参数值对应的字符串,需要再通过 atof,atoi,atol 转为对应的数据类型。
- 关于键值对保存到配置文件,只有 WritePrivateProfileString 这种字符串数据类型,需要通过 _snprintf 将要保存的参数数据类型转为字符串格式再进行保存。 _snprintf 的格式说明和 printf 基本相同。